#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "sock.hpp"

class select_server
{
public:
    select_server(uint16_t port = 8888)
    :_port(port)
    ,_fds(std::vector<int>(sizeof(fd_set) * 8, -1)){}

    ~select_server(){_listen_sock.Close();}

    void init()
    {
        _listen_sock.Socket();
        _listen_sock.Bind(_port);
        _listen_sock.Listen();
        _fds[0] = _listen_sock.GetSock();
    }

    void accepter()
    {
        std::string client_ip;
        uint16_t client_port;
        int sock = _listen_sock.Accept(&client_ip, &client_port);
        if(sock < 0)
            std::cout << "accept fail" << std::endl;

        // 这时候不能直接读取，需要将sock添加到_fds中，继续select
        _fds.push_back(sock);
    }

    void recver(int index)
    {
        int sock = _fds[index];
        std::string buffer(1024, 0);
        ssize_t n = recv(sock, &buffer[0], buffer.size(), 0);
        if(n == -1)
        {
            perror("revc fail");
            _fds[index] = -1;
            close(sock);
            return;
        }
        else if(n == 0)
        {
            std::cout << "client quit" << std::endl;
            _fds[index] = -1;
            close(sock);
            return;
        }
        std::cout << "[request]" << buffer << std::endl;

        // 不考虑write就绪情况，直接写
        std::string response("[server return]");
        response += buffer;
        send(sock, &response[0], response.size(), 0);
    }

    void some_fd_ok(fd_set &read_fs)
    {
        for(size_t i = 0; i < _fds.size(); ++i)
        {
            if(_fds[i] != -1)
            {
                if(FD_ISSET(_fds[i], &read_fs)) //读取就绪
                {
                    // 读取就绪有两种情况
                    // 一种是_listen_sock接受到了新的连接
                    // 另一种是数据就绪

                    // 1.新的连接
                    if(_fds[i] == _listen_sock.GetSock())
                    {
                        std::cout << "新的连接" << std::endl;
                        accepter();
                    }

                    // 2.数据就绪
                    else
                    {
                        std::cout << "数据就绪" << std::endl;
                        recver(i);
                    }
                }
            }
        }
    }

    void start()
    {
        while(1)
        {
            int max_fd = _fds[0];
            for(size_t i = 0; i < _fds.size(); ++i)
            {
                max_fd = std::max(max_fd, _fds[i]);
            }

            fd_set read_fs;
            FD_ZERO(&read_fs);
            for(size_t i = 0; i < _fds.size(); ++i)
            {
                if(_fds[i] != -1)
                {
                    FD_SET(_fds[i], &read_fs);
                }
            }

            struct timeval tv = {3, 0};

            // int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
            // select的第一个参数表示所等待的所有fd中最大的fd + 1
            // select的第二个以及三，四个参数类似，是输入输出型参数，为位图结构
            // 输入时表示哪些fd需要内核关心，输出时表示哪些fd就绪了
            // select的第五个参数为nullptr时表示阻塞等待，为{0, 0}时表示非阻塞等待
            // 为{m, 0}时表示m秒内阻塞，否则返回一次，返回的时候表示剩余时间
            // select的返回值n大于0时表示有n个fd就绪，等于0时表示timeout返回，n小于0时，等待失败
            int n = select(max_fd + 1, &read_fs, nullptr, nullptr, &tv);
            if(n > 0)
            {
                some_fd_ok(read_fs);
            }
            else if(n == 0)
            {
                std::cout << "time out" << std::endl;
            }
            else
            {
                perror("select err");
            }
        }
    }
private:
    uint16_t _port;
    sock _listen_sock;
    std::vector<int> _fds;
};